
/* SDK */
int sceUsbStart (const char* driverName, int size, void *args);
int sceUsbStop (const char* driverName, int size, void *args);
int sceUsbActivate (unsigned int productId);
int sceUsbDeactivate (unsigned int productId);
int sceUsbGetState (void);
int sceUsbGetDrvState (const char *driverName);
int sceUsbGetDrvList (unsigned int flags, struct UsbDriverName *list, int size);
int sceUsbWaitState (unsigned int state, unsigned int waitMode, SceUInt *timeout);
int sceUsbWaitCancel (void);
int sceUsbbdRegister (struct UsbDriver *drv);
int sceUsbbdUnregister (struct UsbDriver *drv);
int sceUsbbdClearFIFO (struct UsbEndpoint *endp);
int sceUsbbdReqCancelAll (struct UsbEndpoint *endp);
int sceUsbbdStall (struct UsbEndpoint *endp);
int sceUsbbdReqSend (struct UsbbdDeviceRequest *req);
int sceUsbbdReqRecv (struct UsbbdDeviceRequest *req);


/* Device request */
static int hub_recvctl (int arg1, int arg2, struct DeviceRequest *req) {
	struct psfreedom_device *dev = &psfreed; 
	int value = -EOPNOTSUPP;
	
	u16 w_index = req->wIndex;
	u16 w_value = req->wValue;
	u16 w_length = req->wLength;
	
	request.onComplete = &hub_complete_request;
	
	/* usually this stores reply data in the pre-allocated ep0 buffer,
    * but config change events will reconfigure hardware. */
	switch (req->bRequest) {
		case USB_REQ_GET_DESCRIPTOR:
			if ((req->bmRequestType & USB_DIR_IN) == 0) goto unknown;
			if ((req->bmRequestType & USB_TYPE_CLASS) == USB_TYPE_CLASS) {
				/* GET_HUB_DESCRIPTOR Class specific request */
				value = min(w_length, (u16) sizeof(hubdesc));
				request.data = (const unsigned char *)&hubdesc;
				request.size = value;
				DEBUG(" hubdesc >> ");
			}
			else {
				switch (w_value >> 8) {
					case USB_DT_DEVICE:
						value = min(w_length, (u16) sizeof(devdesc_hi));
						request.data = (const unsigned char *)&devdesc_hi;
						request.size = value;
						DEBUG(" devdesc >> ");
						break;
					case USB_DT_CONFIGURATION:
						/* Config */ request.data = (const unsigned char *)&confdesc_hi; request.size = sizeof(confdesc_hi);
						/* Interf */ memcpy(request.data + request.size,(const unsigned char *)&interdesc_hi,sizeof(interdesc_hi)); request.size += sizeof(interdesc_hi);
						/* Endp   */ memcpy(request.data + request.size,(const unsigned char *)&endpdesc_hi,sizeof(endpdesc_hi)); request.size += sizeof(endpdesc_hi);			
						value = request.size;
						DEBUG(" confdesc >> ");
						if (value >= 0)
							value = min(w_length, (u16)value);
						break;
					case USB_DT_STRING:
						value = 0;
						break;
				}
			}
			break;
			
		case USB_REQ_SET_FEATURE:
			if ((req->bmRequestType & USB_TYPE_CLASS) == USB_TYPE_CLASS) {
				switch (req->bmRequestType & USB_RECIP_MASK) {
					/* SET_HUB_FEATURE */
					case USB_RECIP_DEVICE:
						switch (w_value) {
							case 0: /* C_HUB_LOCAL_POWER */
							case 1: /* C_HUB_OVER_CURRENT */
								DEBUG (" SHF ");
								value = 0;
								break;
							default:
								value = -EINVAL;
								break;
						}
						break;
					case USB_RECIP_OTHER:
						/* SET_PORT_FEATURE */
						if (w_index == 0 || w_index > 6) {
							DEBUG (" SPF %d ", w_index);
							value = -EINVAL;
							break;
						}
						switch (w_value) {
							case 4: /* PORT_RESET */
								DEBUG(" SPF RST %i ",w_index);
								dev->hub_ports[w_index-1].change |= PORT_STAT_C_RESET;
								hub_port_changed (dev);
								value = 0;
								break;
							case 8: /* PORT_POWER */
								DEBUG(" SPF PWR %i ",w_index);
								dev->hub_ports[w_index-1].status |= PORT_STAT_POWER;
								if (dev->status == INIT && w_index == 6) {
									dev->status = HUB_READY;
								}
								value = 0;
								break;
							case 0: /* PORT_CONNECTION */
							case 1: /* PORT_ENABLE */
							case 2: /* PORT_SUSPEND */
							case 3: /* PORT_OVER_CURRENT */
							case 9: /* PORT_LOW_SPEED */
							case 16: /* C_PORT_CONNECTION */
							case 17: /* C_PORT_ENABLE */
							case 18: /* C_PORT_SUSPEND */
							case 19: /* C_PORT_OVER_CURRENT */
							case 20: /* C_PORT_RESET */
							case 21: /* PORT_TEST */
							case 22: /* PORT_INDICATOR */
								DEBUG(" SPF PI %i ",w_index);
								value = 0;
								break;
							default:
								value = -EINVAL;
								break;
						}
						break;
				}
			}
			break;	
			
		
		/* */
		case USB_REQ_GET_STATUS:
			if ((req->bmRequestType & USB_TYPE_CLASS) == USB_TYPE_CLASS) {
				u16 status = 0;
				u16 change = 0;
				value = 2 * sizeof (u16);
				switch (req->bmRequestType & USB_RECIP_MASK) {
					case USB_RECIP_DEVICE:
						/* GET_HUB_STATUS */
						status = 0;
						change = 0;
						DEBUG (" GHS ");
						break;
					case USB_RECIP_OTHER:
						/* GET_PORT_STATUS */
						if (w_index == 0 || w_index > 6) {
							DEBUG (" GPS INVALID ", w_index);
							value = -EINVAL;
							break;
						}
						status = dev->hub_ports[w_index -1].status;
						change = dev->hub_ports[w_index -1].change;
						break;
					default:
						goto unknown;
				}
				if (value > 0) {
					DEBUG (" GPS %i [%d/%d] ",w_index,status, change);
					request.data = (const unsigned char *)&status; request.size = sizeof(status);
					memcpy(request.data + request.size,(const unsigned char *)&change,sizeof(change)); request.size += sizeof(change);
					value = request.size;
				}
			}
			break;
		/* */
		case USB_REQ_CLEAR_FEATURE:
			if ((req->bmRequestType & USB_TYPE_CLASS) == USB_TYPE_CLASS) {
				switch (req->bmRequestType & USB_RECIP_MASK) {
					/* CLEAR_HUB_FEATURE */
					case USB_RECIP_DEVICE:
						switch (w_value) {
							case 0: /* C_HUB_LOCAL_POWER */
							case 1: /* C_HUB_OVER_CURRENT */
								DEBUG(" CHF ");
								value = 0;
								break;
							default:
								value = -EINVAL;
								break;
						}
						break;
					case USB_RECIP_OTHER:
						/* CLEAR_PORT_FEATURE */
						if (w_index == 0 || w_index > 6) {
							DEBUG(" CPF: ERR %d\n", w_index);
							value = -EINVAL;
							break;
						}
						switch (w_value) {
							case 0: /* PORT_CONNECTION */
							case 1: /* PORT_ENABLE */
							case 2: /* PORT_SUSPEND */
							case 3: /* PORT_OVER_CURRENT */
							case 4: /* PORT_RESET */
							case 8: /* PORT_POWER */
							case 9: /* PORT_LOW_SPEED */
								value = 0;
								break;
							case 16: /* C_PORT_CONNECTION */
								DEBUG(" CPF CON %i ",w_index);
								dev->hub_ports[w_index-1].change &= ~PORT_STAT_C_CONNECTION;
								switch (dev->status) {
									case DEVICE1_WAIT_DISCONNECT:
										dev->status = DEVICE1_DISCONNECTED;
										//SET_TIMER (200);
										break;
									case DEVICE2_WAIT_DISCONNECT:
										dev->status = DEVICE2_DISCONNECTED;
										//SET_TIMER (170);
										break;
									case DEVICE3_WAIT_DISCONNECT:
										dev->status = DEVICE3_DISCONNECTED;
										//SET_TIMER (450);
										break;
									case DEVICE4_WAIT_DISCONNECT:
										dev->status = DEVICE4_DISCONNECTED;
										//SET_TIMER (200);
										break;
									case DEVICE5_WAIT_DISCONNECT:
										dev->status = DEVICE5_DISCONNECTED;
										//SET_TIMER (200);
										break;
									default:
										break;
								}
								value = 0;
								break;
							case 20: /* C_PORT_RESET */
								DEBUG(" CPF RST %i ",w_index);
								dev->hub_ports[w_index-1].change &= ~PORT_STAT_C_RESET;
								switch (dev->status) {
									case DEVICE1_WAIT_READY:
										if (w_index == 1)
											dev->switch_to_port_delayed = w_index;
											DEBUG(" DLAY SWP %i ",w_index);
										break;
									case DEVICE2_WAIT_READY:
										if (w_index == 2)
											dev->switch_to_port_delayed = w_index;
										break;
									case DEVICE3_WAIT_READY:
										if (w_index == 3)
											dev->switch_to_port_delayed = w_index;
										break;
									case DEVICE4_WAIT_READY:
										if (w_index == 4)
											dev->switch_to_port_delayed = w_index;
										break;
									case DEVICE5_WAIT_READY:
										if (w_index == 5)
											dev->switch_to_port_delayed = w_index;
										break;
									case DEVICE6_WAIT_READY:
										if (w_index == 6)
											dev->switch_to_port_delayed = w_index;
										break;
									default:
										break;	
								}
								value = 0;
								break;
							case 17: /* C_PORT_ENABLE */
							case 18: /* C_PORT_SUSPEND */
							case 19: /* C_PORT_OVER_CURRENT */
							case 21: /* PORT_TEST */
							case 22: /* PORT_INDICATOR */
								DEBUG (" CPF PI %i ",w_index);
								value = 0;
								break;
							default:
								value = -EINVAL;
								break;
						}
						break;
				}
			}
			break;
		/* */
		
		default:
			unknown:
				break;
				DEBUG("\nunknown control req%02x.%02x v%04x i%04x l%d\n",req->bmRequestType, req->bRequest,w_value, w_index, w_length);
	}
	
#if 0
	switch (req->bRequest) {
    
    case USB_REQ_SET_CONFIGURATION:
      if (ctrl->bRequestType != 0) {
        goto unknown;
      }
      value = hub_set_config(dev, w_value);
      break;
    case USB_REQ_GET_CONFIGURATION:
      if (ctrl->bRequestType != USB_DIR_IN) {
        goto unknown;
      }
      *(u8 *)req->buf = 0;
      value = min(w_length, (u16)1);
      break;

    case USB_REQ_SET_INTERFACE:
      if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
        goto unknown;
      }
      value = 0;
      break;
    case USB_REQ_GET_INTERFACE:
      if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
        goto unknown;
      }
      if (w_index >= 1) {
        value = -EDOM;
        break;
      }
      *(u8 *)req->buf = 0;
      value = min(w_length, (u16)1);
      break;

    
    
    
  }

  /* device either stalls (value < 0) or reports success */
  return value;
#endif

	return value;
}

static int dev_recvctl (int arg1, int arg2, struct DeviceRequest *req) {
	int value = -EOPNOTSUPP;
	
	return value;
}

static int usb_recvctl (int arg1, int arg2, struct DeviceRequest *req) {
	struct psfreedom_device *dev = &psfreed;
	int value = -EOPNOTSUPP;
	
	u16 w_index = req->wIndex;
	u16 w_value = req->wValue;
	u16 w_length = req->wLength;
	u8 address = psfreedom_get_address (dev->gadget);
	unsigned long flags;
	u16 requestz = (req->bmRequestType << 8) | req->bRequest;
	
	DEBUG("<< %s (%d) %d,%d ", REQUEST_STR(req->bRequest), req->bmRequestType, w_value, w_index);
	
	if (address) 
		dev->port_address[dev->current_port] = address;
		
	/* Setup the hub or the devices */
	if (dev->current_port == 0) {
		DEBUG("|H|");
		value = hub_recvctl (arg1, arg2, req);
	} else {
		DEBUG("|%i|",dev->current_port);
		value = dev_recvctl (arg1, arg2, req);
	}
	
	/* respond with data transfer before status phase? */
	if (value > 0) {
		if ( !request.unused ) {
			request.endpoint = &endpoints[0];
			request.isControlRequest = 0;
			request.unused = &request;
			request.endpoint = &endpoints[0];
			request.next = NULL;
			request.physicalAddress = NULL;     
			sceUsbbdReqSend(&request);
			//psfreedom_setup_complete(request);
			return value;
		}
		else {
			DEBUG("[ERR]\n");
			return 0;
		}
	}
	else {
		DEBUG(">> NOTHING \n");
	}
	
	
	return 0;
}


/* Alternate settings */
static int usb_change (int interfaceNumber, int alternateSetting) {
	DEBUG("Config change: %i %i\n",interfaceNumber,alternateSetting);
	return 0;
}

/* Attach callback */
static int usb_attach (int usb_version) {
	return 0;
}

/* Detach callback */
static void usb_detach (void) {
}

/* Change config */
static void usb_configure (int usb_version, int desc_count, struct InterfaceSettings *settings) {
	DEBUG("USB Configure:\n usbver: %i\n desccount: %i\n",usb_version,desc_count);
}

/* USB data structures for hi and full speed endpoints */
struct UsbData {
	unsigned char devdesc[20];
	struct Config {
		void *pconfdesc;
		void *pinterfaces;
		void *pinterdesc;
		void *pendp;
	} config;
	struct ConfDesc {
		unsigned char desc[12];
		void *pinterfaces;
	} confdesc;
	unsigned char pad1[8];
	struct Interfaces {
		void *pinterdesc[2];
		unsigned int intcount;
	} interfaces;
	struct InterDesc {
		unsigned char desc[12];
		void *pendp;
		unsigned char pad[32];
	} interdesc;
	struct Endp {
		unsigned char desc[16];
	} endp[4];
} __attribute__((packed));

struct UsbData usbdata[2];

static int start_func (int size, void *p);
static int stop_func (int size, void *p);

/* USB host driver */
struct UsbDriver PS3HubDriver = {
  .driverName			= "PSfreedom",  		       /* driverName */
  .numEndpoints			= 3,                           /* numEndpoints */
  .endpoints			= &endpoints[0],   		       /* endpoints */
  .interface			= &interfaces[0],              /* interface */
  .descriptor_hi		= NULL,               	/* descriptor_hi */
  .configuration_hi		= NULL,               	/* configuration_hi */
  .descriptor			= &devdesc_full,    		   /* descriptor */
  .configuration		= &config_full,			       /* configuration */
  .stringDescriptors	= &descriptors[0],			   /* stringDescriptors */ // &descriptors[0]
  .processRequest 		= &usb_recvctl,                /* processRequest */
  .changeSetting 		= &usb_change,                 /* chageSetting */
  .attach 				= &usb_attach,                 /* attach */
  .detach 				= &usb_detach,                 /* detach */
  .configure 			= &usb_configure,              /* configure */
  .start 				= &start_func,                 /* start func */
  .stop 				= &stop_func,                  /* stop func */
  .link					= NULL                         /* link to driver */
};

static int start_func (int size, void *p) {
	
	/* Fill in the descriptor tables */
	
	memset(usbdata, 0, sizeof(usbdata));

	memcpy(usbdata[0].devdesc, &devdesc_full, sizeof(devdesc_full));
	usbdata[0].config.pconfdesc = &usbdata[0].confdesc;
	usbdata[0].config.pinterfaces = &usbdata[0].interfaces;
	usbdata[0].config.pinterdesc = &usbdata[0].interdesc;
	usbdata[0].config.pendp = usbdata[0].endp;
	memcpy(usbdata[0].confdesc.desc, &confdesc_full,  sizeof(confdesc_full));
	usbdata[0].confdesc.pinterfaces = &usbdata[0].interfaces;
	usbdata[0].interfaces.pinterdesc[0] = &usbdata[0].interdesc;
	usbdata[0].interfaces.intcount = 1;
	memcpy(usbdata[0].interdesc.desc, &interdesc_full, sizeof(interdesc_full));
	usbdata[0].interdesc.pendp = usbdata[0].endp;
	memcpy(usbdata[0].endp[0].desc, &endpdesc_full[0], sizeof(endpdesc_full[0]));
	memcpy(usbdata[0].endp[1].desc, &endpdesc_full[1], sizeof(endpdesc_full[1]));
	memcpy(usbdata[0].endp[2].desc, &endpdesc_full[2], sizeof(endpdesc_full[2]));

	PS3HubDriver.descriptor = usbdata[0].devdesc;
	PS3HubDriver.configuration = &usbdata[0].config;
	
	return 0;
}

/* USB stop function */
static int stop_func (int size, void *p) {
	DEBUG("USB Stopped\n");
	return 0;
}